\section{GDB调试工具}
\begin{table}[!htbp]
    \caption{常用的GDB调试命令}
    \begin{tabular}{p{0.2\textwidth}p{0.2\textwidth}p{0.6\textwidth}}
        \toprule
        命令            & 缩写 & 命令说明                                                                                 \\
        \midrule
        list            & l    & 显示多行源代码                                                                           \\
        break           & b    & 设置断点,程序运行到断点的位置会停下来                                                    \\
        info            & i    & 描述程序的状态                                                                           \\
        run             & r    & 开始运行程序直到断点处                                                                   \\
        display         & disp & 跟踪查看某个变量,每次停下来都显示它的值                                                  \\
        step            & s    & 执行下一条语句,如果该语句为函数调用,则进入函数执行其中的第一条语句                       \\
        next            & n    & 执行下一条语句,如果该语句为函数调用,不会进入函数内部执行(即不会一步步地调试函数内部语句) \\
        print           & p    & 打印内部变量值                                                                           \\
        continue        & c    & 继续程序的运行,直到遇到下一个断点                                                        \\
        set var name=v  &      & 设置变量的值                                                                             \\
        start           & st   & 开始执行程序,在main函数的第一条语句前面停下来                                            \\
        file            &      & 装入需要调试的程序                                                                       \\
        kill            & k    & 终止正在调试的程序                                                                       \\
        watch           &      & 监视变量值的变化                                                                         \\
        backtrace       & bt   & 产看函数调用信息(堆栈)                                                                   \\
        frame           & f    & 查看栈帧                                                                                 \\
        call 函数(参数) &      & 调用程序中可见的函数，并传递参数给它。如：\bashinline{call gdb_test(55)}                 \\
        until           &      & 运行程序直到退出循环体,\bashinline{until+行号}运行直到指定行                             \\
        quit            & q    & 退出GDB环境                                                                              \\
        \bottomrule
    \end{tabular}
\end{table}
gdb常见命令：
\begin{itemize}
    \item list:列出源代码文件中的指定内容
          \begin{itemize}
              \item \bashinline{list linenum}：显示程序第\bashinline{linenum}行周围的源程序。
              \item \bashinline{list <function>}：显示函数名为\bashinline{function}的函数的源程序。
              \item \bashinline{list ,}：显示当前行后面的源程序。
              \item \bashinline{list -}：显示当前行前面的源程序。
          \end{itemize}
    \item run命令:在gdb中，运行程序使用run命令。在程序运行前，我们可以设置如下4方面的工作环境：
          \begin{enumerate}
              \item 程序运行参数:\bashinline{set args} 可指定运行时参数，如：\bashinline{set args 10 20 30 40 50}；show args 命令可以查看设置好的运行参数。
              \item \bashinline{path dir} 可设定程序的运行路径；how paths可查看程序的运行路径；
              \item \bashinline{set environment varname=value}用于设置环境变量，如\bashinline{set env USER=baohua}；\bashinline{show environment varname}则用于查看环境变量。
              \item 工作目录。\bashinline{cd dir} 相当于shell的cd命令；pwd 显示当前所在的目录。程序的输入输出\bashinline{info terminal}用于显示程序用到的终端的模式；gdb中也可以使用重定向控制程序输出，如\bashinline{run > outfile}；tty命令可以指定输入输出的终端设备，如：\bashinline{tty /dev/ttyS1} 。
          \end{enumerate}
    \item \bashinline{break}：在gdb中用break命令来设置断点，设置断点的方法包括：
          \begin{itemize}
              \item \bashinline{break function}：在进入指定函数时停住，C++中可以使用\bashinline{class::function}或 \bashinline{function(type, type)} 格式来指定函数名。
              \item \bashinline{break linenum}：在指定\bashinline{linenum}行停住。
              \item \bashinline{break +offset / break -offset}：在当前行号的前面或后面的offset行停住，offiset为自然数。
              \item \bashinline{break filename:linenum}：在源文件filename的linenum行处停住。
              \item \bashinline{break filename:function}：在源文件filename的function函数的入口处停住。
              \item \bashinline{break *address}:在程序运行的内存地址处停住。
              \item \bashinline{break}：break命令没有参数时，表示在下一条指令处停住。
              \item \bashinline{break ... if <condition>}：\bashinline{...}可以是上述的 \bashinline{break <linenum>} , \bashinline{break +offset / break –offset} 中的参数，condition表示条件，在条件成立时停住。比如在循环体中，可以设置\bashinline{break if i=100}，表示当i为100时停住程序。
          \end{itemize}
          查看断点时，可使用info命令，如 \bashinline{info breakpoints n}、\bashinline{info break n}（n表示断点号）。

    \item 单步命令：在调试过程中，next命令用于单步执行。next的单步不会进入函数的内部，与next对应的step（缩写s）命令则在单步执行一个函数时，会进入其内部。
          单步执行的更复杂用法包括：
          \begin{itemize}
              \item \bashinline{step count}：单步跟踪，如果有函数调用，则进入该函数（进入函数的前提是，此函数被编译有debug信息）。step后面不加count表示一条条地执行，加表示执行后面的count条指令，然后再停住。
              \item \bashinline{next count}：单步跟踪，如果有函数调用，它不会进入该函数。同样地，next后面不加count表示一条条地执行，加表示执行后面的count条指令，然后再停住。
              \item \bashinline{set step-mode} ：设置step-mode模式，\bashinline{set step-mode on}用于打开step-mode模式，这样，在进行单步跟踪时，程序不会因为没有debug信息而不停住，这个参数的设置可便于查看机器码。\bashinline{set step-mod off} 用于关闭step-mode模式。
          \end{itemize}
    \item \bashinline{finish}：运行程序，直到当前函数完成返回，并打印函数返回时的堆栈地址和返回值及参数值等信息。
    \item \bashinline{until}：（缩写u）一直在循环体内执行单步，退不出来是一件令人烦恼的事情，until命令可以运行程序直到退出循环体。
    \item stepi（缩写si）和nexti（缩写ni）：stepi和nexti用于单步跟踪一条机器指令，一条程序代码有可能由数条机器指令完成，stepi和nexti可以单步执行机器指令。 另外，运行“display/i pc”命令后，单步跟踪会在打出程序代码的同时打出机器指令，即汇编代码。
    \item \bashinline{continue}：当程序被停住后，可以使用continue命令（缩写c，fg命令同continue命令）恢复程序的运行直到程序结束，或到达下一个断点，命令格式为：
          \begin{itemize}
              \item continue [ignore-count]
              \item c [ignore-count]
              \item fg [ignore-count]
          \end{itemize}
          ignore-count表示忽略其后多少次断点。 假设我们设置了函数断点add()，并watch i，则在continue过程中，每次遇到add()函数或i发生变化，程序就会停住。
    \item print命令：在调试程序时，当程序被停住时，可以使用print命令（缩写为p），或是同义命令inspect来查看当前程序的运行数据。print命令的格式是：
          \begin{itemize}
              \item \bashinline{print expr}
              \item \bashinline{print /f expr}
          \end{itemize}
          \bashinline{expr}是表达式，是被调试的程序中的表达式，\bashinline{f}是输出的格式，比如，如果要把表达式按16进制的格式输出，那么就是\bashinline{/x}。在表达式中，有几种GDB所支持的操作符，它们可以用在任何一种语言中，“@”是一个和数组有关的操作符，“::”指定一个在文件或是函数中的变量，\bashinline{type addr}表示一个指向内存地址\bashinline{addr}的类型为\bashinline{type}的一个对象。下面演示了查看sum[]数组的值的过程：
          当需要查看一段连续内存空间的值的时间，可以使用GDB的“@”操作符，“@”的左边是第一个内存地址，“@”的右边则是想查看内存的长度。例如如下动态申请的内存：
          int *array = (int *) malloc (len * sizeof (int));
          在GDB调试过程中这样显示出这个动态数组的值：p *array@len。
          print的输出格式包括：
          \begin{itemize}
              \item    x 按十六进制格式显示变量。
              \item    d 按十进制格式显示变量。
              \item    u 按十六进制格式显示无符号整型。
              \item    o 按八进制格式显示变量。
              \item    t 按二进制格式显示变量。
              \item    a 按十六进制格式显示变量。
              \item    c 按字符格式显示变量。
              \item    f 按浮点数格式显示变量。
          \end{itemize}
          我们可用display命令设置一些自动显示的变量，当程序停住时，或是单步跟踪时，这些变量会自动显示。 如果要修改变量，如x的值，可使用如下命令：
          \bashinline{print x=4}
          当用GDB的print查看程序运行时的数据时，每一个print都会被GDB记录下来。GDB会以$1，$2，\$3 …这样的方式为每一个print命令编号。我们可以使用这个编号访问以前的表达式，如\$1。
    \item \bashinline{watch}： watch一般来观察某个表达式（变量也是一种表达式）的值是否有变化了，如果有变化，马上停住程序。我们有下面的几种方法来设置观察点：
          \begin{enumerate}
              \item \bashinline{watch expr}：为表达式（变量）expr设置一个观察点。一旦表达式值有变化时，马上停住程序。
              \item \bashinline{rwatch expr} ：当表达式（变量）expr被读时，停住程序。
              \item \bashinline{awatch expr} ：当表达式（变量）的值被读或被写时，停住程序。
              \item \bashinline{info watchpoints} ：列出当前所设置了的所有观察点。 下面演示了观察i并在连续运行next时一旦发现i变化，i值就会显示出来的过程。
          \end{enumerate}
    \item \bashinline{examine}： 我们可以使用examine命令（缩写为x）来查看内存地址中的值。examine命令的语法如下所示：\bashinline{x/n/f/u addr }
          \bashinline{addr} 表示一个内存地址。“x/”后的n、f、u都是可选的参数，n 是一个正整数，表示显示内存的长度，也就是说从当前地址向后显示几个地址的内容；f 表示显示的格式，如果地址所指的是字符串，那么格式可以是s，如果地址是指令地址，那么格式可以是i；u 表示从当前地址往后请求的字节数，如果不指定的话，GDB默认是4字节。u参数可以被一些字符代替：b表示单字节，h表示双字节，w表示四字节，g表示八字节。当我们指定了字节长度后，GDB会从指定的内存地址开始，读写指定字节，并把其当作一个值取出来。n、f、u这3个参数可以一起使用，例如命令“x/3uh 0x54320”表示从内存地址0x54320开始以双字节为1个单位（h）、16进制方式（u）显示3个单位（3）的内存。
    \item \bashinline{jump}： 一般来说，被调试程序会按照程序代码的运行顺序依次执行，但是GDB也提供了乱序执行的功能，也就是说，GDB可以修改程序的执行顺序，从而让程序随意跳跃。这个功能可以由GDB的jump命令：\bashinline{jump linespec} 来指定下一条语句的运行点。 \bashinline{linespec} 可以是文件的行号，可以是file:line格式，也可以是+num这种偏移量格式，表示下一条运行语句从哪里开始。\bashinline{jump address} 这里的 \bashinline{address} 是代码行的内存地址。 注意，jump命令不会改变当前的程序栈中的内容，所以，如果使用jump从一个函数跳转到另一个函数，当跳转到的函数运行完返回，进行出栈操作时必然会发生错误，这可能导致意想不到的结果，所以最好只用jump在同一个函数中进行跳转。
    \item \bashinline{signal}：使用singal命令，可以产生一个信号量给被调试的程序，如中断信号“Ctrl+C”。这非常方便于程序的调试，可以在程序运行的任意位置设置断点，并在该断点用GDB产生一个信号量，这种精确地在某处产生信号的方法非常有利于程序的调试。 signal命令的语法是：\bashinline{signal signal}，UNIX的系统信号量通常从1到15，所以\bashinline{signal}取值也在这个范围。
    \item \bashinline{return}：如果在函数中设置了调试断点，在断点后还有语句没有执行完，这时候我们可以使用return命令强制函数忽略还没有执行的语句并返回。
          \begin{itemize}
              \item \bashinline{return}
              \item \bashinline{return expression}
          \end{itemize}
          上述return命令用于取消当前函数的执行，并立即返回，如果指定了 \bashinline{expression} ，那么该表达式的值会被作为函数的返回值。
    \item \bashinline{call}：用于强制调用某函数， \bashinline{call expr }表达式中可以一是函数，以此达到强制调用函数的目的，它会显示函数的返回值（如果函数返回值不是void）。 其实，前面介绍的print命令也可以完成强制调用函数的功能。

    \item \bashinline{info}：在调试时用来查看寄存器、断点、观察点和信号等信息。要查看寄存器的值，可以使用如下命令：
          \begin{itemize}
              \item  \bashinline{info registers} ：（查看除了浮点寄存器以外的寄存器）
              \item \bashinline{info all-registers} ：（查看所有寄存器，包括浮点寄存器）
              \item \bashinline{info registers regname ...} ：（查看所指定的寄存器）
          \end{itemize}
          要查看断点信息，可以使用如下命令：
          \begin{itemize}
              \item \bashinline{info break} 列出当前所设置的所有观察点，使用如下命令：
              \item \bashinline{info watchpoints} 查看有哪些信号正在被GDB检测，使用如下命令：
              \item \bashinline{info signals}
              \item \bashinline{info handle} 也可以使用 \bashinline{info line}命令来查看源代码在内存中的地址。
              \item \bashinline{info threads} 可以看多线程。info line后面可以跟行号、函数名、文件名:行号、文件名:函数名等多种形式，例如下面的命令会打印出所指定的源码在运行时的内存地址：\bashinline{info line tst.c:func}。
          \end{itemize}
    \item \bashinline{set scheduler-locking off|on|step}
          \begin{itemize}
              \item off 不锁定任何线程，也就是所有线程都执行，这是默认值。
              \item on 只有当前被调试程序会执行。
              \item step 在单步的时候，除了next过一个函数的情况以外，只有当前线程会执行。与多线程调试相关的命令还包括：
                    \begin{itemize}
                        \item \bashinline{thread ID}：切换当前调试的线程为指定ID的线程。
                        \item \bashinline{break thread_test.c:123 thread all} ：在所有线程中相应的行上设置断点
                        \item \bashinline{thread apply ID1 ID2 command }让一个或者多个线程执行GDB命令command。
                        \item \bashinline{thread apply all command} ：让所有被调试线程执行GDB命令command。
                    \end{itemize}
          \end{itemize}
          \bashinline{disassemble}
          disassemble命令用于反汇编，它可被用来查看当前执行时的源代码的机器码，其实际上只是把目前内存中的指令dump出来。下面的示例用于查看函数func的汇编代码：
\end{itemize}
